﻿#!/usr/bin/env python

import gtk
import time
import os
import sys
from gtk import StatusIcon
from threading import Thread
from ping import Pinger
import socket

gtk.gdk.threads_init()
class TrayStatusIcon(StatusIcon):
    def __init__(self):
        StatusIcon.__init__(self)
        self.offline_count = 0
        self.quit_pending = False
        menu = '''
			<ui>
			 <menubar name="Menubar">
			  <menu action="Menu">
			   <menuitem action="Quit"/>
			  </menu>
			 </menubar>
			</ui>
		'''
        actions = [('Menu', None, 'Menu'), ('Quit', None, '_Quit', None, 'Force Pinger to quit', self.on_quit)]
        ag = gtk.ActionGroup('Actions')
        ag.add_actions(actions)
        self.manager = gtk.UIManager()
        self.manager.insert_action_group(ag, 0)
        self.manager.add_ui_from_string(menu)
        self.menu = self.manager.get_widget('/Menubar/Menu/Quit').props.parent

        try:
            self.manager.get_widget('/Menubar/Menu/Quit').get_children()[1].set_from_stock(gtk.STOCK_QUIT, gtk.ICON_SIZE_MENU)
        except:
            pass
        
        self.connect('popup-menu', self.on_popup_menu)
        #self.connect('activate', self.on_quit)
        self.set_offline()
        self.set_visible(True)
    
    def on_popup_menu(self, status, button, time):
        self.menu.popup(None, None, None, button, time)
    
    def on_quit(self, data):
        self.set_visible(False)
        self.quit_pending = True
        quit()
    
    def set_offline(self, caption = ''):
        self.offline_count += 1
        if self.offline_count > 0: 
            self.set_from_file('offline-icon.ico')
            if caption != '': self.set_tooltip(caption)
            self.set_tooltip(str(self.offline_count))
    
    def set_online(self, caption = ''):
        if self.offline_count > 0: 
            self.offline_count -= 1
            self.set_tooltip(str(self.offline_count))
        if self.offline_count == 0: 
            self.set_from_file('online-icon.ico')
            if caption != '': self.set_tooltip(caption)

class HostPinger(Pinger):
    def __init__(self, host):
        Pinger.__init__(self, host, 1)
    
    def ping(self):
        print 'HostPinger.ping()'
        Pinger.ping(self)
        self.sent = self.num - 1
        
        return len(self.times.values()) == 0

class PingThread(Thread):
    def __init__(self, host, status_icon = None):
        Thread.__init__(self)
        self.host = host
        print >>sys.stderr, 'Ping thread for host %s created' % self.host
        try:
            self.pinger = HostPinger(self.host)
        except socket.herror:
            self.pinger = None
        except socket.gaierror:
            print >>sys.stderr, "Failed to resolve %s" % self.host
            self.pinger = None
        except Exception, e:
            raise e
        self.status_icon = status_icon
        self.last_status = False
        self.quit_pending = False
    
    def run(self):
        while not self.quit_pending and not self.status_icon.quit_pending:
            # print >>sys.stderr, 'Ping %s' % self.host
            if self.pinger != None and self.pinger.ping():
                if not self.last_status and (self.status_icon != None) and callable(getattr(self.status_icon, 'set_online')):
                    self.status_icon.set_online()
                self.last_status = True
            else:
                if self.last_status and (self.status_icon != None) and callable(getattr(self.status_icon, 'set_offline')):
                    self.status_icon.set_offline()
                self.last_status = False
            time.sleep(5)
    
    def terminate(self):
        if callable(getattr(self.status_icon, 'set_online')): self.status_icon.set_online()
        self.quit_pending = True
    
    def __del__(self):
        self.terminate()

class Pynger:
    def __init__(self, icon = None):
        self.hosts = {}
        self.default_icon = icon
    
    def register_host(self, host):
        host_id = None
        matches = [key for key, val in self.hosts.iteritems() if val.host == host]
        
        print >>sys.stderr, "Registering %s" % host
        if matches:
            host_id = matches[0]
        else:
            if self.hosts.keys():
                host_id = str(max([int(x) for x in self.hosts.keys()]) + 1)
            else:
                host_id = '0'
            self.hosts[host_id] = PingThread(host, self.default_icon)
            self.hosts[host_id].start()
        
        return host_id
    
    def unregister_host(self, host_id):
        if self.hosts.has_key(host_id): 
            if self.hosts[host_id].isAlive(): self.hosts[host_id].terminate()
            del self.hosts[host_id]
    
    def __del__(self):
        for thread in self.hosts.itervalues():
            if thread.isAlive(): thread.terminate()

if __name__ == '__main__':
    p = Pynger(TrayStatusIcon())
    hosts = [ '192.168.163.99' ]
    host_ids = [p.register_host(host) for host in hosts]
    # print >>sys.stderr, 'Registered %s with id %s' % (host, host_id)
    
    gtk.gdk.threads_enter()
    gtk.main()
    gtk.gdk.threads_leave()